package br.com.sgdee.dao.impl;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceException;
import javax.persistence.Query;
import javax.persistence.TypedQuery;

import org.jboss.logging.Logger;

import br.com.sgdee.entidades.SGDEEntidadeGeneric;


public abstract class GenericoDAOImpl<T> {

	private EntityManager em;


	private static final Logger LOGGER = Logger.getLogger(GenericoDAOImpl.class);

	private Class<T> entityClass;

	public GenericoDAOImpl(Class<T> entityClass) {
		this.entityClass = entityClass;
	}
	
	public GenericoDAOImpl(Class<T> entityClass, EntityManager em) {
		this.entityClass = entityClass;
		this.em = em;
	}
	
	protected void salvar(T o) {

		try {
			getEm().persist(o);
			getEm().flush();

		} catch (Exception e) {
			LOGGER.error(e.getMessage(), e);
			throw new PersistenceException(e);
		}
	}

	protected T alterar(T o) {

		try {
			o = getEm().merge(o);
			getEm().flush();

		} catch (Exception e) {
			LOGGER.error(e.getMessage(), e);
			throw new PersistenceException(e);
		}

		return o;
	}

	protected void excluir(T o) {

		try {
			getEm().remove(getEm().getReference(entityClass, ((SGDEEntidadeGeneric) o).getId()));
			getEm().flush();

		} catch (Exception e) {
			LOGGER.error(e.getMessage(), e);
			throw new PersistenceException(e);
		}
	}
	
	protected void excluirLista(List<T> list) {
		
		try {
			
			for (T o : list) {

				getEm().remove(getEm().getReference(entityClass, ((SGDEEntidadeGeneric) o).getId()));
			}
			getEm().flush();
		} catch (Exception e) {
			
			LOGGER.error(e.getMessage(), e);
			throw new PersistenceException(e);
		}
	}
	
    public List<T> recuperarTodos() {
    	
    	StringBuilder hql = new StringBuilder();
    	hql.append("SELECT n ");
    	hql.append("FROM ");
    	hql.append("NivelAcesso n ");
    	
        return recuperarListaObjetos(hql.toString());
    }

	protected List<T> recuperarListaObjetos(String hql, Object... parametros) {

		try {
			return getQuery(hql, parametros).getResultList();

		} catch (Exception e) {

			LOGGER.error(e.getMessage(), e);
			throw new PersistenceException(e);
		}
	}

	protected List<T> recuperarListaObjetos(String hql, List<Object> parametros) {
		
		try {
			return getQuery(hql, parametros).getResultList();

		} catch (NoResultException e) {
			
			return null;
		} catch (Exception e) {

			LOGGER.error(e.getMessage(), e);
			throw new PersistenceException(e);
		}
	}
	
	@SuppressWarnings("rawtypes")
	protected List recuperarListaObjetosNoType(String hql, List<Object> parametros) {
		
		try {
			return getQueryNoType(hql, parametros).getResultList();
			
		} catch (NoResultException e) {
			
			return null;
		} catch (Exception e) {
			
			LOGGER.error(e.getMessage(), e);
			throw new PersistenceException(e);
		}
	}

	@SuppressWarnings("unchecked")
	protected List<T> recuperarListaObjetosNativa(String sql, Object... parametros) {

		try {
			return getQueryNativa(sql, null, parametros).getResultList();

		} catch (Exception e) {

			LOGGER.error(e.getMessage(), e);
			throw new PersistenceException(e);
		}
	}

	protected T recuperarObjetoSingleResult(String hql, Object... parametros) {

		try {
			return getQuery(hql, parametros).getSingleResult();

		} catch (NoResultException e) {
			return null;

		} catch (Exception e) {

			LOGGER.error(e.getMessage(), e);
			throw new PersistenceException(e);
		}
	}
	
	protected T recuperarObjetoSingleResult(String hql, List<Object> parametros) {
		
		try {
			return getQuery(hql, parametros).getSingleResult();
			
		} catch (NoResultException e) {
			return null;
			
		} catch (Exception e) {
			
			LOGGER.error(e.getMessage(), e);
			throw new PersistenceException(e);
		}
	}
	
	protected Integer recuperarQuantidadeObjeto(String hql, Object... parametros) {

		try {
			return (Integer) getQuantidadeQuery(hql, null, parametros).getSingleResult();

		} catch (NoResultException e) {
			return null;

		} catch (Exception e) {
			LOGGER.error(e.getMessage(), e);
			throw new PersistenceException(e);
		}
	}

	protected T recuperarObjeto(String hql, Object... parametros) {

		try {
			return getQuery(hql, null, parametros).getSingleResult();

		} catch (NoResultException e) {
			return null;

		} catch (Exception e) {
			LOGGER.error(e.getMessage(), e);
			throw new PersistenceException(e);
		}
	}
	
	/**
	 * Parametros com Map<Class, Object> 
	 * 
	 * @param hql
	 * @param parametros
	 * @return
	 */
	protected T recuperarObjeto(String hql, List<Object> parametros) {

		try {
			return getQuery(hql, parametros).getSingleResult();

		} catch (NoResultException e) {
			return null;

		} catch (Exception e) {
			LOGGER.error(e.getMessage(), e);
			throw new PersistenceException(e);
		}
	}

	protected Object recuperarObjetoNativa(String hql, Object... parametros) {

		try {
			return getQueryNativa(hql, null, parametros).getSingleResult();

		} catch (NoResultException e) {
			return null;

		} catch (Exception e) {
			LOGGER.error(e.getMessage(), e);
			throw new PersistenceException(e);
		}
	}

	protected Integer executarUpdateNativa(String hql, Object... parametros) {

		try {
			return getQueryNativa(hql, null, parametros).executeUpdate();

		} catch (NoResultException e) {
			return null;

		} catch (Exception e) {
			LOGGER.error(e.getMessage(), e);
			throw new PersistenceException(e);
		}
	}
	
	protected Integer executarUpdateNativa(String hql, List<Object> parametros) {
		
		try {
			return getQueryNativa(hql, parametros).executeUpdate();
			
		} catch (NoResultException e) {
			return null;
			
		} catch (Exception e) {
			LOGGER.error(e.getMessage(), e);
			throw new PersistenceException(e);
		}
	}

	private TypedQuery<T> getQuery(String hql, Object... parametros) {

		TypedQuery<T> query = getEm().createQuery(hql, this.entityClass);

		setQueryParametros(query, parametros);

		return query;
	}
	
	/**
	 * Parametros com Map<Class, Object> 
	 * 
	 * @param hql
	 * @return
	 */
	private TypedQuery<T> getQuery(String hql, List<Object> parametros ) {
		
		TypedQuery<T> query = getEm().createQuery(hql, this.entityClass);
		
		setQueryParametros(query, parametros);
		
		return query;
	}
	
	
	private Query getQueryNoType(String hql, List<Object> parametros ) {
		
		Query query = getEm().createQuery(hql, this.entityClass);
		
		setQueryParametros(query, parametros);
		
		return query;
	}
	
	private Query getQuantidadeQuery(String hql, Object... parametros) {

		Query query = getEm().createQuery(hql, Object.class);

		setQueryParametros(query, parametros);

		return query;
	}

	private Query getQueryNativa(String sql, Object... parametros) {

		Query query = getEm().createNativeQuery(sql);

		setQueryParametros(query, parametros);

		return query;
	}
	
	private Query getQueryNativa(String sql, List<Object> parametros) {
		
		Query query = getEm().createNativeQuery(sql, Boolean.class);
		
		setQueryParametros(query, parametros);
		
		return query;
	}

	private void setQueryParametros(Query query, Object... parametros) {
		
		if (parametros != null && parametros.length > 0) {

			int i = 1;
			for (Object item : parametros) {

				if (item != null) {
					query.setParameter(i, item);
					i++;
				}
			}
		}
	}
	
	/**
	 * Map<Class, Object>
	 * 
	 * @param query
	 * @param parametros
	 */
	private void setQueryParametros(Query query, List<Object> parametros) {
		
		
		int index = 1;
		for (Object tipoValor : parametros) {
			
			query.setParameter(index,  tipoValor.getClass().cast(tipoValor));
			index++;
		}
	}

	public EntityManager getEm() {
		return em;
	}
}
